/*
 * sparse_vec.h
 *
 *  Created on: Feb 28, 2011
 *      Author: tqlong
 */

#ifndef SPARSE_VEC_H_
#define SPARSE_VEC_H_

#ifndef ARMA_NAMESPACE_BEGIN
#define ARMA_NAMESPACE_BEGIN namespace arma {
#endif

#ifndef ARMA_NAMESPACE_END
#define ARMA_NAMESPACE_END   }
#endif

#include <vector>
#include <iostream>
#include <cassert>

ARMA_NAMESPACE_BEGIN;

/** A sparse vector implementation, incomplete now
 *  but have enough functionality to use in SVM algorithm
 */
class sparse_vec
{
  int dim_;                                            // the dimension
  std::vector<double> nz_;                             // non-zeros
  std::vector<int> idx_;                               // indices of non-zeros
public:
  /** Number of elements */
  unsigned int n_elem;                                 // same as dim_, used for for-loop

  /** Constructor */
  sparse_vec(int d = 1) : dim_(d), n_elem(d) {}

  /** Number of non zeros */
  int nnz() const { return nz_.size(); }

  /** Dimension */
  int dim() const { return dim_; }

  /** Randomize the elements with normal/uniform distribution */
  const sparse_vec& randn();
  const sparse_vec& randu();

  /** Fill vector with 0/1 */
  const sparse_vec& zeros();
  const sparse_vec& ones();

  /** Arithmetic operators */
  sparse_vec operator/(double s) const;
  sparse_vec operator*(double s) const;
  sparse_vec operator-(const sparse_vec& v) const;
  sparse_vec operator+(const sparse_vec& v) const;
  sparse_vec operator-() const;
  sparse_vec operator+() const;
  const sparse_vec& operator+=(const sparse_vec& v);
  const sparse_vec& operator-=(const sparse_vec& v);
  const sparse_vec& operator*=(double s);
  const sparse_vec& operator/=(double s);

  /** Element access methods */
  double get(int i) const;
  double& ref(int i);
  const std::vector<double>& nz() const { return nz_; }
  const std::vector<int>& idx() const { return idx_; }
  std::vector<double>& nz() { return nz_; }
  std::vector<int>& idx() { return idx_; }

  /** Find an index in idx_ array */
  bool find(int i, int& pos) const;

  /** Norm of a vector : l-1, l-2, l-infinity*/
  friend double norm(const sparse_vec& v, int ell);
  friend double norm(const sparse_vec& v, const char* type);

  /** Dot product */
  friend double dot(const sparse_vec& u, const sparse_vec& v);
};

double norm(const sparse_vec& v, int ell);
double norm(const sparse_vec& v, const char* type);
double dot(const sparse_vec& u, const sparse_vec& v);

ARMA_NAMESPACE_END;

/** Formatted printing */
std::ostream& operator<<(std::ostream&, const arma::sparse_vec&);

/** Save/load functions */
template <typename S>
void save(S& os, const arma::sparse_vec& v)
{
//  std::cerr<< "v="<<v<<"\n";
  os << v.nnz() << " ";
  for (int i = 0; i < v.nnz(); i++)
    os << v.idx()[i] << ":" << v.nz()[i] << " ";
}

template <typename S>
void load(S& f, arma::sparse_vec& x)
{
  int nnz;
  f >> nnz;
  x.zeros();
  for (int k = 0; k < nnz; k++) {
    unsigned int i;
    double val;
    char c;
    f >> i >> c >> val;
//    std::cout << "i = " << i << c << "val = " << val << " ";
    assert(c == ':' && i < x.n_elem && i >= 0);
    x.idx().push_back(i);
    x.nz().push_back(val);
  }
}


/** Arithmetic operators */

arma::sparse_vec operator*(double s, const arma::sparse_vec& v);  // TODO

#endif /* SPARSE_VEC_H_ */
